package com.db2excel.impl;

import com.db2excel.DataReader;
import com.workin.excel.exp.module.Header;
import com.workin.excel.exp.module.HeaderFactory;
import com.workin.excel.exp.module.MultiHeader;
import com.workin.excel.exp.module.SimpleHeader;
import com.workin.excel.exp.util.BigDataExportExcelUtil;
import ognl.OgnlException;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.*;
import java.text.ParseException;
import java.util.List;
import java.util.Map;

/**
 * Created by wxg on 2019/1/4 11:09
 */
public class DataReaderImpl implements DataReader {
    private Workbook workbook;
    private Header header;

    /**
     * Use InputStream to construct. Suitable for simple headers.
     *
     * @param excelXml From an XML file, this XLM file should define the header format for exporting Excel
     */
    public DataReaderImpl(InputStream excelXml) throws JDOMException, IOException, ParseException, OgnlException {
        SAXBuilder builder = new SAXBuilder();
        Document document = builder.build(excelXml);
        Element root = document.getRootElement();
        Header header = HeaderFactory.getInstance().getHeader(root);
        this.workbook = BigDataExportExcelUtil.getWorkbook(header);
        this.header = header;
    }

    /**
     * Use header object to construct. Suitable for dynamic headers.
     *
     * @param header A header object
     */
    public DataReaderImpl(Header header) throws JDOMException, IOException, ParseException, OgnlException {
        this.workbook = BigDataExportExcelUtil.getWorkbook(header);
        this.header = header;
    }

    /**
     * Use xml character string to construct.
     *
     * @param excelXml Strings in XML format.
     */
    public DataReaderImpl(String excelXml) throws JDOMException, IOException, ParseException, OgnlException {
        SAXBuilder builder = new SAXBuilder();
        Document document = builder.build(new StringReader(excelXml));
        Element root = document.getRootElement();
        Header header = HeaderFactory.getInstance().getHeader(root);
        this.workbook = BigDataExportExcelUtil.getWorkbook(header);
        this.header = header;
    }

    @Override
    public DataReader append(List<Map<String, Object>> dataList) throws ParseException, OgnlException {
        if (this.header != null && this.workbook != null) {
            BigDataExportExcelUtil.appendDataToWorkbook(workbook, dataList, header);
            setAutoColumnSize(header, workbook);
        }
        return this;
    }

    @Override
    public void export(OutputStream outputStream) throws IOException {
        if (workbook != null) {
            try {
                workbook.write(outputStream);
            } finally {
                outputStream.flush();
                outputStream.close();
            }

        }
    }

    /**
     * Auto column width
     */
    private void setAutoColumnSize(Header header, Workbook workbook) {
        try {
            Sheet sheet = workbook.getSheet(header.getTitle());
            if (header.isMultiHeader()) {
                List<MultiHeader> li = header.getMultiHeaders().get(1);
                for (int i = 0, len = li.size(); i < len; i++) {
                    sheet.setColumnWidth(i, li.get(i).getText().length() * 600);
                }
            } else {
                List<SimpleHeader> li = header.getSimpleHeaders();
                for (int i = 0, len = li.size(); i < len; i++) {
                    sheet.setColumnWidth(i, li.get(i).getText().length() * 600);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Header buildHeader(Map<String, String> title, List<Map<String, String>> headers) {
        return buildHeader(title, headers, null);
    }

    /**
     * create excel headers.
     *
     * @param headers      headers(necessary). Must contain "key".
     *                     The key must be the same as the data key in the data source
     * @param multiHeaders Multi headers.
     */
    public static Header buildHeader(Map<String, String> title, List<Map<String, String>> headers, List<List<Map<String, String>>> multiHeaders) {
        StringBuilder sb = new StringBuilder();
        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        sb.append("<excel>");

        sb.append("<title ");
        for (Map.Entry<String, String> entry : title.entrySet()) {
            String pa = entry.getKey() + "= \"" + entry.getValue() + "\" ";
            sb.append(pa);
        }
        sb.append(" />");

        if (multiHeaders == null || multiHeaders.isEmpty()) {
            sb.append("<headers type=\"default\" style=\"center\">");
            for (Map<String, String> h : headers) {
                String headerStr = "<header key=\"" + h.get("key") + "\" text=\"" + h.get("text") + "\" style=\"center\" />";
                sb.append(headerStr);
            }
            sb.append("</headers>");
        } else {
            sb.append("<multi-headers>");
            for (List<Map<String, String>> cols : multiHeaders) {
                sb.append("<row>");
                for (Map<String, String> col : cols) {
                    sb.append("<column ");
                    for (Map.Entry<String, String> entry : col.entrySet()) {
                        String pa = entry.getKey() + "=\"" + entry.getValue() + "\" ";
                        sb.append(pa);
                    }
                    sb.append(" style=\"center\"/>");
                }
                sb.append("</row>");
            }
            sb.append("</multi-headers>");

            sb.append("<headers type=\"default\" style=\"center\">");
            for (Map<String, String> h : headers) {
                sb.append("<header ");
                for (Map.Entry<String, String> entry : h.entrySet()) {
                    String pa = entry.getKey() + "=\"" + entry.getValue() + "\" ";
                    sb.append(pa);
                }
                sb.append(" style=\"center\"/>");
            }
            sb.append("</headers>");
        }
        sb.append("</excel>");

        InputStream ins = new ByteArrayInputStream(sb.toString().getBytes());
        SAXBuilder builder = new SAXBuilder();
        Document root;
        try {
            root = builder.build(ins);
            Element element = root.getRootElement();
            Header header = HeaderFactory.getInstance().getHeader(element);
            return header;
        } catch (JDOMException | IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}
